package com.dd;

import ohos.aafwk.ability.Ability;
import ohos.agp.colors.RgbColor;
import ohos.agp.components.AttrSet;
import ohos.agp.components.Button;
import ohos.agp.components.Component;
import ohos.agp.components.ComponentState;
import ohos.agp.components.element.Element;
import ohos.agp.components.element.ShapeElement;
import ohos.agp.components.element.StateElement;
import ohos.agp.render.Canvas;
import ohos.agp.utils.Color;
import ohos.agp.utils.Rect;
import ohos.app.Context;

public class CircularProgressButton extends Button {
    public static final int IDLE_STATE_PROGRESS = 0;
    public static final int ERROR_STATE_PROGRESS = -1;
    public static final int SUCCESS_STATE_PROGRESS = 100;
    public static final int INDETERMINATE_STATE_PROGRESS = 50;

    private StrokeGradientDrawable background;
    private CircularAnimatedDrawable mAnimatedDrawable;
    private CircularProgressDrawable mProgressDrawable;

    private ColorStateList mIdleColorState;
    private ColorStateList mCompleteColorState;
    private ColorStateList mErrorColorState;

    private StateElement mIdleStateDrawable;
    private StateElement mCompleteStateDrawable;
    private StateElement mErrorStateDrawable;

    private StateManager mStateManager;
    private State mState;
    private String mIdleText;
    private String mCompleteText;
    private String mErrorText;
    private String mProgressText;

    private int mColorProgress;
    private int mColorIndicator;
    private int mColorIndicatorBackground;
    private Element mIconComplete;
    private Element mIconError;
    private int mStrokeWidth;
    private int mPaddingProgress;
    private float mCornerRadius;
    private boolean mIndeterminateProgressMode;
    private boolean mConfigurationChanged;

    private enum State {
        PROGRESS,
        IDLE,
        COMPLETE,
        ERROR
    }

    private int mMaxProgress;
    private int mProgress;

    private boolean mMorphingInProgress;

    public CircularProgressButton(Context context) {
        this(context, null, null);
    }

    public CircularProgressButton(Context context, AttrSet attrs) {
        this(context, attrs, null);
    }

    public CircularProgressButton(Context context, AttrSet attrs, String defStyle) {
        super(context, attrs, defStyle);
        init(context, attrs);
    }

    private void init(Context context, AttrSet attributeSet) {
        mStrokeWidth = dp2px(4);

        initAttributes(context, attributeSet);

        mMaxProgress = 100;
        mState = State.IDLE;
        mStateManager = new StateManager(this);

        setText(mIdleText);

        initIdleStateDrawable();
        initCompleteStateDrawable();
        initErrorStateDrawable();
        setBackgroundCompat(mIdleStateDrawable);
    }

    private void initErrorStateDrawable() {
        int colorPressed = getPressedColor(mErrorColorState);

        StrokeGradientDrawable drawablePressed = createDrawable(colorPressed);
        mErrorStateDrawable = new StateElement();
        mErrorStateDrawable.addState(
                new int[] {ComponentState.COMPONENT_STATE_PRESSED}, drawablePressed.getGradientDrawable());
        mErrorStateDrawable.addState(
                new int[] {ComponentState.COMPONENT_STATE_EMPTY}, background.getGradientDrawable());
    }

    private void initCompleteStateDrawable() {
        int colorPressed = getPressedColor(mCompleteColorState);

        StrokeGradientDrawable drawablePressed = createDrawable(colorPressed);
        mCompleteStateDrawable = new StateElement();

        mCompleteStateDrawable.addState(
                new int[] {ComponentState.COMPONENT_STATE_PRESSED}, drawablePressed.getGradientDrawable());
        mCompleteStateDrawable.addState(
                new int[] {ComponentState.COMPONENT_STATE_EMPTY}, background.getGradientDrawable());
    }

    private void initIdleStateDrawable() {
        int colorNormal = getNormalColor(mIdleColorState);
        int colorPressed = getPressedColor(mIdleColorState);
        int colorFocused = getFocusedColor(mIdleColorState);
        int colorDisabled = getDisabledColor(mIdleColorState);
        if (background == null) {
            background = createDrawable(colorNormal);
        }

        StrokeGradientDrawable drawableDisabled = createDrawable(colorDisabled);
        StrokeGradientDrawable drawableFocused = createDrawable(colorFocused);
        StrokeGradientDrawable drawablePressed = createDrawable(colorPressed);
        mIdleStateDrawable = new StateElement();
        mIdleStateDrawable.addState(
                new int[] {ComponentState.COMPONENT_STATE_PRESSED}, drawablePressed.getGradientDrawable());
        mIdleStateDrawable.addState(
                new int[] {ComponentState.COMPONENT_STATE_FOCUSED}, drawableFocused.getGradientDrawable());
        mIdleStateDrawable.addState(
                new int[] {ComponentState.COMPONENT_STATE_DISABLED}, drawableDisabled.getGradientDrawable());
        mIdleStateDrawable.addState(new int[] {ComponentState.COMPONENT_STATE_EMPTY}, background.getGradientDrawable());
    }

    private int getNormalColor(ColorStateList colorStateList) {
        return colorStateList.getColorForState(new int[] {ComponentState.COMPONENT_STATE_EMPTY}, 0);
    }

    private int getPressedColor(ColorStateList colorStateList) {
        return colorStateList.getColorForState(new int[] {ComponentState.COMPONENT_STATE_PRESSED}, 0);
    }

    private int getFocusedColor(ColorStateList colorStateList) {
        return colorStateList.getColorForState(new int[] {ComponentState.COMPONENT_STATE_FOCUSED}, 0);
    }

    private int getDisabledColor(ColorStateList colorStateList) {
        return colorStateList.getColorForState(new int[] {ComponentState.COMPONENT_STATE_DISABLED}, 0);
    }

    private StrokeGradientDrawable createDrawable(int color) {
        ShapeElement drawable = new ShapeElement();
        drawable.setRgbColor(RgbColor.fromArgbInt(color));
        drawable.setCornerRadius(mCornerRadius);
        drawable.setStroke(dp2px(4), RgbColor.fromArgbInt(0xff0099cc));
        StrokeGradientDrawable strokeGradientDrawable = new StrokeGradientDrawable(drawable);
        strokeGradientDrawable.setStrokeColor(color);
        strokeGradientDrawable.setStrokeWidth(mStrokeWidth);

        return strokeGradientDrawable;
    }

    private void initAttributes(Context context, AttrSet attributeSet) {
        mIdleText = AttrUtils.getStringFromAttr(attributeSet, "cpb_textIdle", "");
        mCompleteText = AttrUtils.getStringFromAttr(attributeSet, "cpb_textComplete", "");
        mErrorText = AttrUtils.getStringFromAttr(attributeSet, "cpb_textError", "");
        mProgressText = AttrUtils.getStringFromAttr(attributeSet, "cpb_textProgress", "");

        mIconComplete = AttrUtils.getElementFromAttr(attributeSet, "cpb_iconComplete", null);
        mIconError = AttrUtils.getElementFromAttr(attributeSet, "cpb_iconError", null);
        mCornerRadius = AttrUtils.getDimensionFromAttr(attributeSet, "cpb_cornerRadius", 0);
        mPaddingProgress = AttrUtils.getDimensionFromAttr(attributeSet, "cpb_paddingProgress", 0);

        int blue = Color.BLUE.getValue();
        int white = Color.WHITE.getValue();
        int grey = Color.GRAY.getValue();

        int cpbSelectorIdlePressed = AttrUtils.getColorFromAttr(attributeSet, "cpb_selectorIdle_pressed", 0xff0000aa);
        int cpbSelectorIdleFocused = AttrUtils.getColorFromAttr(attributeSet, "cpb_selectorIdle_focused", 0xff0000aa);
        int cpbSelectorIdleDisabled =
                AttrUtils.getColorFromAttr(attributeSet, "cpb_selectorIdle_disabled", 0xff7d7d7d);
        int cpbSelectorIdle = AttrUtils.getColorFromAttr(attributeSet, "cpb_selectorIdle", 0xff0000ff);

        mIdleColorState = new ColorStateList();
        mIdleColorState.addState(new int[] {ComponentState.COMPONENT_STATE_PRESSED}, cpbSelectorIdlePressed);
        mIdleColorState.addState(new int[] {ComponentState.COMPONENT_STATE_FOCUSED}, cpbSelectorIdleFocused);
        mIdleColorState.addState(new int[] {ComponentState.COMPONENT_STATE_DISABLED}, cpbSelectorIdleDisabled);
        mIdleColorState.addState(new int[] {ComponentState.COMPONENT_STATE_EMPTY}, cpbSelectorIdle);

        int selectorCompletePressed =
                AttrUtils.getColorFromAttr(attributeSet, "cpb_selectorComplete_pressed", 0xff00aa00);
        int selectorCompleteFocused =
                AttrUtils.getColorFromAttr(attributeSet, "cpb_selectorComplete_focused", 0xff00aa00);
        int selectorCompleteDisabled =
                AttrUtils.getColorFromAttr(attributeSet, "cpb_selectorComplete_disabled", 0xff7d7d7d);
        int selectorComplete = AttrUtils.getColorFromAttr(attributeSet, "cpb_selectorComplete", 0xff00ff00);

        mCompleteColorState = new ColorStateList();
        mCompleteColorState.addState(new int[] {ComponentState.COMPONENT_STATE_PRESSED}, selectorCompletePressed);
        mCompleteColorState.addState(new int[] {ComponentState.COMPONENT_STATE_FOCUSED}, selectorCompleteFocused);
        mCompleteColorState.addState(new int[] {ComponentState.COMPONENT_STATE_DISABLED}, selectorCompleteDisabled);
        mCompleteColorState.addState(new int[] {ComponentState.COMPONENT_STATE_EMPTY}, selectorComplete);

        int selectorErrorPressed = AttrUtils.getColorFromAttr(attributeSet, "cpb_selectorError_pressed", 0xffaa0000);
        int selectorErrorFocused = AttrUtils.getColorFromAttr(attributeSet, "cpb_selectorError_focused", 0xffaa0000);
        int selectorErrorDisabled = AttrUtils.getColorFromAttr(attributeSet, "cpb_selectorError_disabled", 0xff7d7d7d);
        int selectorError = AttrUtils.getColorFromAttr(attributeSet, "cpb_selectorError", 0xffff0000);

        mErrorColorState = new ColorStateList();
        mErrorColorState.addState(new int[] {ComponentState.COMPONENT_STATE_PRESSED}, selectorErrorPressed);
        mErrorColorState.addState(new int[] {ComponentState.COMPONENT_STATE_FOCUSED}, selectorErrorFocused);
        mErrorColorState.addState(new int[] {ComponentState.COMPONENT_STATE_FOCUSED}, selectorErrorDisabled);
        mErrorColorState.addState(new int[] {ComponentState.COMPONENT_STATE_EMPTY}, selectorError);

        mColorProgress = AttrUtils.getColorFromAttr(attributeSet, "cpb_colorProgress", 0xffffffff);
        mColorIndicator = AttrUtils.getColorFromAttr(attributeSet, "cpb_colorIndicator", 0xffff5454);
        mColorIndicatorBackground = AttrUtils.getColorFromAttr(attributeSet, "cpb_colorIndicatorBackground", grey);
        onDraw();
    }

    protected void onDraw() {
        addDrawTask(
                new DrawTask() {
                    @Override
                    public void onDraw(Component component, Canvas canvas) {
                        if ((mMorphingInProgress || mState == State.PROGRESS) && background != null) {
                            background.getGradientDrawable().drawToCanvas(canvas);
                        }

                        if (mProgress > 0 && mState == State.PROGRESS && !mMorphingInProgress) {
                            if (mIndeterminateProgressMode) {
                                drawIndeterminateProgress(canvas);
                            } else {
                                drawProgress(canvas);
                            }
                        }
                    }
                },
                DrawTask.BETWEEN_BACKGROUND_AND_CONTENT);
    }

    private void drawIndeterminateProgress(Canvas canvas) {
        if (mAnimatedDrawable == null) {
            int offset = (getWidth() - getHeight()) / 2;
            mAnimatedDrawable = new CircularAnimatedDrawable(mColorIndicator, mStrokeWidth);
            int left = offset + mPaddingProgress;
            int right = getWidth() - offset - mPaddingProgress;
            int bottom = getHeight() - mPaddingProgress;
            int top = mPaddingProgress;
            mAnimatedDrawable.onBoundsChange(new Rect(left, top, right, bottom));
            mAnimatedDrawable.setComponent(this);
            mAnimatedDrawable.start();
            mAnimatedDrawable.drawToCanvas(canvas);
        } else {
            mAnimatedDrawable.drawToCanvas(canvas);
        }
    }

    private void drawProgress(Canvas canvas) {
        if (mProgressDrawable == null) {
            int offset = (getWidth() - getHeight()) / 2;
            int size = getHeight() - mPaddingProgress * 2;
            mProgressDrawable = new CircularProgressDrawable(size, mStrokeWidth, mColorIndicator);
            int left = offset + mPaddingProgress;
            mProgressDrawable.onBoundsChange(new Rect(left, mPaddingProgress, left, mPaddingProgress));
        }
        float sweepAngle = (360f / mMaxProgress) * mProgress;
        mProgressDrawable.setSweepAngle(sweepAngle);
        mProgressDrawable.drawToCanvas(canvas);
    }

    public boolean isIndeterminateProgressMode() {
        return mIndeterminateProgressMode;
    }

    public void setIndeterminateProgressMode(boolean indeterminateProgressMode) {
        this.mIndeterminateProgressMode = indeterminateProgressMode;
    }

    private MorphingAnimation createMorphing() {
        setBackground(new ShapeElement());
        mMorphingInProgress = true;

        MorphingAnimation animation = new MorphingAnimation(this, background);
        animation.setFromCornerRadius(mCornerRadius);
        animation.setToCornerRadius(mCornerRadius);

        animation.setFromWidth(getWidth());
        animation.setToWidth(getWidth());

        if (mConfigurationChanged) {
            animation.setDuration(MorphingAnimation.DURATION_INSTANT);
        } else {
            animation.setDuration(MorphingAnimation.DURATION_NORMAL);
        }

        mConfigurationChanged = false;

        return animation;
    }

    private MorphingAnimation createProgressMorphing(float fromCorner, float toCorner, int fromWidth, int toWidth) {
        setBackground(new ShapeElement());
        mMorphingInProgress = true;

        MorphingAnimation animation = new MorphingAnimation(this, background);
        animation.setFromCornerRadius(fromCorner);
        animation.setToCornerRadius(toCorner);

        animation.setPadding(mPaddingProgress);

        animation.setFromWidth(fromWidth);
        animation.setToWidth(toWidth);

        if (mConfigurationChanged) {
            animation.setDuration(MorphingAnimation.DURATION_INSTANT);
        } else {
            animation.setDuration(MorphingAnimation.DURATION_NORMAL);
        }

        mConfigurationChanged = false;

        return animation;
    }

    private void morphToProgress() {
        setWidth(getWidth());
        setText(mProgressText);

        MorphingAnimation animation = createProgressMorphing(mCornerRadius, getHeight(), getWidth(), getHeight());

        animation.setFromColor(getNormalColor(mIdleColorState));
        animation.setToColor(mColorProgress);

        animation.setFromStrokeColor(getNormalColor(mIdleColorState));
        animation.setToStrokeColor(mColorIndicatorBackground);

        animation.setListener(mProgressStateListener);

        animation.start();
    }

    private OnAnimationEndListener mProgressStateListener =
            new OnAnimationEndListener() {
                @Override
                public void onAnimationEnd() {
                    mMorphingInProgress = false;
                    mState = State.PROGRESS;
                    mStateManager.checkState(CircularProgressButton.this);
                }
            };

    private void morphProgressToComplete() {
        MorphingAnimation animation = createProgressMorphing(getHeight(), mCornerRadius, getHeight(), getWidth());

        animation.setFromColor(mColorProgress);
        animation.setToColor(getNormalColor(mCompleteColorState));

        animation.setFromStrokeColor(mColorIndicator);
        animation.setToStrokeColor(getNormalColor(mCompleteColorState));

        animation.setListener(mCompleteStateListener);

        animation.start();
    }

    private void morphIdleToComplete() {
        MorphingAnimation animation = createMorphing();

        animation.setFromColor(getNormalColor(mIdleColorState));
        animation.setToColor(getNormalColor(mCompleteColorState));

        animation.setFromStrokeColor(getNormalColor(mIdleColorState));
        animation.setToStrokeColor(getNormalColor(mCompleteColorState));

        animation.setListener(mCompleteStateListener);

        animation.start();
    }

    private OnAnimationEndListener mCompleteStateListener =
            new OnAnimationEndListener() {
                @Override
                public void onAnimationEnd() {
                    if (mIconComplete != null) {
                        setText(null);
                        setIcon(mIconComplete);
                    } else {
                        setText(mCompleteText);
                    }
                    mMorphingInProgress = false;
                    mState = State.COMPLETE;
                    setBackgroundCompat(mCompleteStateDrawable);
                    mStateManager.checkState(CircularProgressButton.this);
                }
            };

    private void morphCompleteToIdle() {
        MorphingAnimation animation = createMorphing();

        animation.setFromColor(getNormalColor(mCompleteColorState));
        animation.setToColor(getNormalColor(mIdleColorState));

        animation.setFromStrokeColor(getNormalColor(mCompleteColorState));
        animation.setToStrokeColor(getNormalColor(mIdleColorState));

        animation.setListener(mIdleStateListener);

        animation.start();
    }

    private void morphErrorToIdle() {
        MorphingAnimation animation = createMorphing();

        animation.setFromColor(getNormalColor(mErrorColorState));
        animation.setToColor(getNormalColor(mIdleColorState));

        animation.setFromStrokeColor(getNormalColor(mErrorColorState));
        animation.setToStrokeColor(getNormalColor(mIdleColorState));

        animation.setListener(mIdleStateListener);

        animation.start();
    }

    private OnAnimationEndListener mIdleStateListener =
            new OnAnimationEndListener() {
                @Override
                public void onAnimationEnd() {
                    removeIcon();
                    setText(mIdleText);
                    mMorphingInProgress = false;
                    mState = State.IDLE;
                    setBackgroundCompat(mIdleStateDrawable);
                    mStateManager.checkState(CircularProgressButton.this);
                }
            };

    private void morphIdleToError() {
        MorphingAnimation animation = createMorphing();

        animation.setFromColor(getNormalColor(mIdleColorState));
        animation.setToColor(getNormalColor(mErrorColorState));

        animation.setFromStrokeColor(getNormalColor(mIdleColorState));
        animation.setToStrokeColor(getNormalColor(mErrorColorState));

        animation.setListener(mErrorStateListener);

        animation.start();
    }

    private void morphProgressToError() {
        MorphingAnimation animation = createProgressMorphing(getHeight(), mCornerRadius, getHeight(), getWidth());

        animation.setFromColor(mColorProgress);
        animation.setToColor(getNormalColor(mErrorColorState));

        animation.setFromStrokeColor(mColorIndicator);
        animation.setToStrokeColor(getNormalColor(mErrorColorState));
        animation.setListener(mErrorStateListener);

        animation.start();
    }

    private OnAnimationEndListener mErrorStateListener =
            new OnAnimationEndListener() {
                @Override
                public void onAnimationEnd() {
                    if (mIconError != null) {
                        setText(null);
                        setIcon(mIconError);
                    } else {
                        setText(mErrorText);
                    }
                    mMorphingInProgress = false;
                    mState = State.ERROR;
                    setBackgroundCompat(mErrorStateDrawable);
                    mStateManager.checkState(CircularProgressButton.this);
                }
            };

    private void morphProgressToIdle() {
        MorphingAnimation animation = createProgressMorphing(getHeight(), mCornerRadius, getHeight(), getWidth());

        animation.setFromColor(mColorProgress);
        animation.setToColor(getNormalColor(mIdleColorState));

        animation.setFromStrokeColor(mColorIndicator);
        animation.setToStrokeColor(getNormalColor(mIdleColorState));
        animation.setListener(
                new OnAnimationEndListener() {
                    @Override
                    public void onAnimationEnd() {
                        removeIcon();
                        setText(mIdleText);
                        mMorphingInProgress = false;
                        mState = State.IDLE;

                        mStateManager.checkState(CircularProgressButton.this);
                        setBackgroundCompat(mIdleStateDrawable);
                    }
                });

        animation.start();
    }

    private void setIcon(Element icon) {
        if (icon != null) {
            int padding = (getWidth() / 2) - (icon.getWidth() / 2);
            setAroundElements(icon, null, null, null);
            setPadding(padding, 0, 0, 0);
        }
    }

    protected void removeIcon() {
        setAroundElements(null, null, null, null);
        setPadding(0, 0, 0, 0);
    }

    public void setBackgroundCompat(Element drawable) {
        setBackground(drawable);
    }

    public void setProgress(int progress) {
        mProgress = progress;

        if (mMorphingInProgress || getWidth() == 0) {
            return;
        }

        mStateManager.saveProgress(this);

        if (mProgress >= mMaxProgress) {
            if (mState == State.PROGRESS) {
                morphProgressToComplete();
            } else if (mState == State.IDLE) {
                morphIdleToComplete();
            }
        } else if (mProgress > IDLE_STATE_PROGRESS) {
            if (mState == State.IDLE) {
                morphToProgress();
            } else if (mState == State.PROGRESS) {
                invalidate();
            }
        } else if (mProgress == ERROR_STATE_PROGRESS) {
            if (mState == State.PROGRESS) {
                morphProgressToError();
            } else if (mState == State.IDLE) {
                morphIdleToError();
            }
        } else if (mProgress == IDLE_STATE_PROGRESS) {
            if (mState == State.COMPLETE) {
                morphCompleteToIdle();
            } else if (mState == State.PROGRESS) {
                morphProgressToIdle();
            } else if (mState == State.ERROR) {
                morphErrorToIdle();
            }
        }
    }

    public int getProgress() {
        return mProgress;
    }

    public void setBackgroundColor(int color) {
        background.getGradientDrawable().setRgbColor(RgbColor.fromArgbInt(color));
    }

    public void setStrokeColor(int color) {
        background.setStrokeColor(color);
    }

    public String getIdleText() {
        return mIdleText;
    }

    public String getCompleteText() {
        return mCompleteText;
    }

    public String getErrorText() {
        return mErrorText;
    }

    public void setIdleText(String text) {
        mIdleText = text;
    }

    public void setCompleteText(String text) {
        mCompleteText = text;
    }

    public void setErrorText(String text) {
        mErrorText = text;
    }

    public void onLayout() {
        setLayoutRefreshedListener(
                new LayoutRefreshedListener() {
                    @Override
                    public void onRefreshed(Component component) {
                        setProgress(mProgress);
                    }
                });
    }

    private int dp2px(float dp) {
        return (int) (getResourceManager().getDeviceCapability().screenDensity / 160 * dp);
    }
}
